package LogParser;

my $pat_ip_address = qr/(\d{1,3} \.
    \d{1,3} \.
    \d{1,3} \.
    \d{1,3})/x;
my $pat_quoted_field = qr/"((?:(?:(?:(?:    # It can be...
    [^"\\])* |  # ...zero or more characters not quote or backslash...
    (?:\\x[0-9a-fA-F][0-9a-fA-F])* | # ...a backslash quoted hexadecimal character...
    (?:\\.*)                         # ...or a backslash escape.
   ))*))"/x;
my $parse_combined = qr/^       # Start at the beginning
     $pat_ip_address \s+        # IP address
     (\S+) \s+                  # Ident
     (\S+) \s+                  # Userid
     \[([^\]]*)\] \s+           # Date and time
     $pat_quoted_field \s+      # Request
     (\d+) \s+                  # Status
     (\-|[\d]+) \s+             # Length of reply or "-"
     $pat_quoted_field \s+      # Referer
     $pat_quoted_field          # User agent
     $                          # End at the end
   /x;

my @fields = qw(ip ident userid time request status length referer ua);

use constant REQUEST_IDX => 4;

sub new {
    my $_class = shift;
    my $class = ref $_class || $_class;
    my $fh = shift || die "LogParser->new([$file | $fh])";
    unless ( ref $fh eq 'GLOB' ) {
        my $file = $fh;
        open($fh, "<", $file) or die "Can't open file $file: $!";
    }
    my $self = [$fh];
    bless $self, $class;
    return $self;
}

sub next {
    my $self = shift;
    my $fh = $self->[0];
    return if eof($fh);
    chomp(my $line = <$fh>);
    my @ent = ($line =~ /$parse_combined/);
    my $ent = { map { $fields[$_] => $ent[$_] } 0..$#fields };
    my $request = delete $ent->{request};
    ($ent->{method}, $ent->{url}) = ($request =~ /(\w+) (.*) HTTP\/1\.1/);
    return $ent;
}

1;
